﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using GClient = Google.GData.Client;
using GExtensions = Google.GData.Extensions;
using System.Collections.ObjectModel;
using System.Reflection;

namespace GSync
{
    public abstract class GoogleDataProvider<EntryType, FeedType, ServiceType> : IDataProvider
        where EntryType : GClient.AtomEntry, new()
        where FeedType : GClient.AtomFeed
        where ServiceType : GClient.Service
    {






        #region abstract methods

        protected FeedType _Feed;
        protected abstract FeedType Feed
        {
            get;
        }

        protected ServiceType _Service;
        protected abstract ServiceType Service
        {
            get;
        }
        protected abstract void CopyToNative(InternalEntry entry, EntryType nativeEntry);
        protected abstract InternalEntry ConvertToInternal(EntryType nativeEntry);
        protected abstract string FeedUri
        { get; }


        #endregion

        #region IDataProvider Members

        public ReadOnlyCollection<InternalEntry> AllRecords
        {
            get
            {
                List<InternalEntry> allEntries = (from EntryType nativeEntry
                                                  in Feed.Entries
                                                  select ConvertToInternal(nativeEntry)).ToList();

                allEntries.RemoveAll(c => c == null);

                return allEntries.AsReadOnly();
            }
        }



        public void AddEntry(InternalEntry entry)
        {
            EntryType newNativeEntry = new EntryType();

            CopyToNative(entry, newNativeEntry);


            Uri feedUri = new Uri(FeedUri);

            EntryType createdEntry = _Service.Insert(feedUri, newNativeEntry) as EntryType;

            entry.ID = createdEntry.Id.AbsoluteUri.Substring(createdEntry.Id.AbsoluteUri.LastIndexOf('/') + 1);
        }



        public void UpdateEntry(InternalEntry entry)
        {
            EntryType targerNativeEntry = FindNativeEntryByID(entry.ID);


            CopyToNative(entry, targerNativeEntry);

            targerNativeEntry = targerNativeEntry.Update() as EntryType;

            for (int i = 0; i < Feed.Entries.Count; i++)
            {
                if (Feed.Entries[i].Id.Equals(targerNativeEntry.Id))
                {
                    Feed.Entries[i] = targerNativeEntry;
                    break;
                }
            }
        }



        public void DeleteEntry(InternalEntry entry)
        {
            EntryType targerNativeEntry = FindNativeEntryByID(entry.ID);
            targerNativeEntry.Delete();
        }

        public bool CanModifyEntryID
        {
            get { return false; }
        }

        #endregion


        protected virtual EntryType FindNativeEntryByID(string id)
        {
            EntryType result = FindNativeEntryByIDOrDefault(id);

            if (result == null)
            {
                throw new ArgumentException(string.Format("Native record with ID='{0}' wasn't found.", id));
            }

            return result;
        }

        protected virtual EntryType FindNativeEntryByIDOrDefault(string id)
        {
            EntryType result = null;

            foreach (EntryType nativeEntry in Feed.Entries)
            {
                if (nativeEntry.Id.AbsoluteUri.EndsWith(id))
                {
                    result = nativeEntry;
                }
            }

            return result;
        }


        #region IDisposable Members

        public void Dispose()
        {
        }

        #endregion
    }
}
